package org.fatewa.engine.hooks;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import jakarta.validation.groups.Default;
import lombok.Data;
import lombok.experimental.Accessors;
import org.fatewa.engine.advice.exception.RayquazaException;
import org.fatewa.engine.design.genius.GenericHooks;
import org.fatewa.engine.genius.base.ValidateScope;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpStatus;

import javax.annotation.PostConstruct;
import java.util.Set;

/**
 * 基于 GenericHooks 实现
 * 两个泛型都为 Object 类型时，可以拦截所有自动生成的接口
 *
 * @author fatewa
 * @see GenericHooks
 */
@Data
@Accessors(fluent = true, chain = true)
public class ValidateHook implements GenericHooks<Object, Object> {

    /**
     * spring 上下文
     */
    private ApplicationContext context;

    /**
     * 校验器
     */
    private Validator validator;

    @PostConstruct
    public void injectValidator() {
        try (ValidatorFactory factory = Validation.buildDefaultValidatorFactory()) {
            this.validator = factory.getValidator();
        }
    }

    @Override
    public void beforeCreate(Object entity, HttpServletRequest request) {
        final Set<ConstraintViolation<Object>> validate = validator.validate(entity,
                Default.class, ValidateScope.Create.class);
        // 校验参数
        this.valid(validate);
    }

    @Override
    public void beforeUpdate(Object entity, HttpServletRequest request) {
        // 手动校验参数
        final Set<ConstraintViolation<Object>> validate = validator.validate(entity,
                Default.class, ValidateScope.Update.class);
        this.valid(validate);
    }

    /**
     * 校验是否存在异常， 如果存在异常，则按照 {@link HttpStatus#BAD_REQUEST} 进行抛出
     *
     * @param validate 是否存在异常
     */
    private void valid(Set<ConstraintViolation<Object>> validate) {
        //noinspection unchecked
        final ConstraintViolation<Object>[] constraintViolations = validate.toArray(new ConstraintViolation[]{});

        // 校验器长度大于 0， 抛出异常，取第一条校验异常消息
        if (!validate.isEmpty()) {
            throw new RayquazaException(HttpStatus.BAD_REQUEST, constraintViolations[0].getMessage());
        }

    }

    /**
     * 提供校验能力的 Hook 优先级为最高
     *
     * @return 运行优先级
     */
    @Override
    public int order() {
        return Integer.MIN_VALUE;
    }

}
